/**
 * Copyright (c) 2018-2028.
 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.sailmi.auth.controller;


import com.baomidou.mybatisplus.core.toolkit.AES;
import com.sailmi.auth.com.sailmi.auth.vo.LoginInfoVo;
import com.sailmi.auth.redis.RedisUtil;
import com.sailmi.common.cache.CacheNames;
import com.sailmi.common.constant.TokenConstant;
import com.sailmi.common.model.auth.AuthInfo;
import com.sailmi.common.model.auth.AuthUser;
import com.sailmi.common.secure.user.IUserProvider;
import com.sailmi.common.tool.api.R;
import com.sailmi.common.tool.support.Kv;
import com.sailmi.common.tool.utils.Func;
import com.sailmi.common.tool.utils.WebUtil;
import com.wf.captcha.SpecCaptcha;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import com.sailmi.auth.granter.ITokenGranter;
import com.sailmi.auth.granter.TokenGranterBuilder;
import com.sailmi.auth.granter.TokenParameter;
import com.sailmi.auth.utils.TokenUtil;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.*;
import java.util.Date;
import java.util.Objects;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
/**
 * 认证模块
 *
 * @author yzh
 */
@Tag(
	name="用户认证接口",
	description = "用户认证接口，以token的方式"
)
@RestController
public class AuthController {
	private static final Log logger = LogFactory.getLog(AuthController.class);
	@Autowired
	private RedisUtil redisUtil;
	@Autowired
	private IUserProvider userProvider;
	private final String SECKEY = "cascSecretKey";

	/**
	 * @param grantType password,用户名密码；message,短信
	 * @param refreshToken
	 * @param tenantId
	 * @param account
	 * @param password
	 * @return
	 */
	@Operation(
		summary = "获取用户token",
		description = "获取用户token,通过用户名与管码"
	)
	@PostMapping("token")
	public R token(@RequestParam(name="grantType",defaultValue = "password", required = false) String grantType,
				   @RequestParam(name="refreshToken",required = false) String refreshToken,
				   @RequestParam(name="tenantId",defaultValue = "000000", required = false) String tenantId,
				   @RequestParam(name="account",required = false) String account,
				   @RequestParam(name="password",required = false) String password) {

		String userType = Func.toStr(WebUtil.getRequest().getHeader(TokenUtil.USER_TYPE_HEADER_KEY), TokenUtil.DEFAULT_USER_TYPE);
		String authHeader = WebUtil.getRequest().getHeader("Authorization");
		TokenParameter tokenParameter = new TokenParameter();

		logger.info("login using tenantid:"+tenantId);
		logger.info("login using account:"+account);
		logger.info("login using password:"+password);
		logger.info("login using granttype:"+grantType);
		logger.info("login using usertype:"+userType);
		logger.info("login using refreshToken:"+refreshToken);
		logger.info("login using authCode:"+authHeader);

		tokenParameter.getArgs()
			.set("tenantId", tenantId)
			.set("account", account)
			.set("password", password)
			.set("grantType", grantType)
			.set("refreshToken", refreshToken)
			.set("userType", userType);

		ITokenGranter granter = TokenGranterBuilder.getGranter(grantType);
		logger.debug("get granter:"+ granter);
		AuthUser userInfo = granter.grant(tokenParameter);
		logger.debug("logined user is:"+userInfo);
		logger.debug("logined user id is:"+userInfo.getUserId());
		if (userInfo  == null) {
			return R.fail(TokenUtil.USER_NOT_FOUND);
		}

		return R.data(TokenUtil.createAuthInfo(userInfo));
	}

	/**
	 * @param loginInfoVo,登录信息
	 * @return
	 */
	@Operation(
		summary = "获取用户token",
		description = "获取用户token,通过用户名与管码"
	)
	@PostMapping("tokenBySec")
	public R tokenBySec(@RequestBody(required = true) LoginInfoVo loginInfoVo) {
		String userType = Func.toStr(WebUtil.getRequest().getHeader(TokenUtil.USER_TYPE_HEADER_KEY), TokenUtil.DEFAULT_USER_TYPE);
		String authHeader = WebUtil.getRequest().getHeader("Authorization");
		TokenParameter tokenParameter = new TokenParameter();
		String tenantId = loginInfoVo.getTenantId();
		String account = loginInfoVo.getAccount();
		String password = loginInfoVo.getPassword();
		String grantType = loginInfoVo.getGrantType();
		String refreshToken = loginInfoVo.getRefreshToken();
		logger.info("login using tenantid:"+tenantId);
		logger.info("login using account:"+account);
		logger.info("login using password:"+password);
		logger.info("login using granttype:"+grantType);
		logger.info("login using usertype:"+userType);
		logger.info("login using refreshToken:"+refreshToken);
		logger.info("login using authCode:"+authHeader);
		try{
			password = AES.decrypt(password,SECKEY);
			logger.info("login using password decrypt:"+password);
		}catch(Exception ec){
			return R.fail(ec.getMessage());
		}
		tokenParameter.getArgs()
			.set("tenantId", tenantId)
			.set("account", account)
			.set("password", password)
			.set("grantType", grantType)
			.set("refreshToken", refreshToken)
			.set("userType", userType);
		ITokenGranter granter = TokenGranterBuilder.getGranter(grantType);
		logger.debug("get granter:"+ granter);
		AuthUser userInfo = granter.grant(tokenParameter);
		logger.debug("logined user is:"+userInfo);
		if (userInfo  == null) {
			return R.fail(TokenUtil.USER_NOT_FOUND);
		}
		return R.data(TokenUtil.createAuthInfo(userInfo));
	}

	/**
	 *
	 * @param grantType password,用户名密码；message,短信
	 * @param refreshToken
	 * @param tenantId
	 * @param account
	 * @param password
	 * @return
	 */
	@PostMapping("login")
	public R login( @RequestBody(required = false) String grantType,
				    @RequestBody(required = false) String refreshToken,
				    @RequestBody(required = false) String tenantId,
				    @RequestBody(required = false) String account,
				    @RequestBody(required = false) String password) {

		String userType = Func.toStr(WebUtil.getRequest().getHeader(TokenUtil.USER_TYPE_HEADER_KEY), TokenUtil.DEFAULT_USER_TYPE);
		String authHeader = WebUtil.getRequest().getHeader("Authorization");

		TokenParameter tokenParameter = new TokenParameter();
		logger.debug("login using tenantid:"+tenantId);
		logger.debug("login using account:"+account);
		logger.debug("login using password:"+password);
		logger.debug("login using granttype:"+grantType);
		logger.debug("login using usertype:"+userType);
		logger.debug("login using refreshToken:"+refreshToken);
		logger.debug("login using authCode:"+authHeader);

		tokenParameter.getArgs().set("tenantId", tenantId)
			.set("account", account)
			.set("password", password)
			.set("grantType", grantType)
			.set("refreshToken", refreshToken)
			.set("userType", userType);

		ITokenGranter granter = TokenGranterBuilder.getGranter(grantType);
		logger.debug("get granter:"+ granter);
		AuthUser userInfo = granter.grant(tokenParameter);
		logger.debug("logined user is:"+userInfo);
		if(userInfo!=null){
			logger.debug("logined user is:"+userInfo.getAccount());
			logger.debug("logined user permission size is:"+userInfo.getPermissions().size());
		}
		if (userInfo == null) {
			return R.fail(TokenUtil.USER_NOT_FOUND);
		}

		return R.data(TokenUtil.createAuthInfo(userInfo));
	}


	/**
	 * 通过微服务方式登录
	 * @param grantType password,用户名密码；message,短信
	 * @param refreshToken
	 * @param tenantId
	 * @param account
	 * @param password
	 * @return
	 */
	@PostMapping("loginRest")
	public R loginRest( @RequestBody(required = false) String grantType,
				    @RequestBody(required = false) String refreshToken,
				    @RequestBody(required = false) String tenantId,
				    @RequestBody(required = false) String account,
				    @RequestBody(required = false) String password) {

		String userType = Func.toStr(WebUtil.getRequest().getHeader(TokenUtil.USER_TYPE_HEADER_KEY), TokenUtil.DEFAULT_USER_TYPE);
		String authHeader = WebUtil.getRequest().getHeader("Authorization");

		TokenParameter tokenParameter = new TokenParameter();
		logger.debug("login using tenantid:"+tenantId);
		logger.debug("login using account:"+account);
		logger.debug("login using password:"+password);
		logger.debug("login using granttype:"+grantType);
		logger.debug("login using usertype:"+userType);
		logger.debug("login using refreshToken:"+refreshToken);
		logger.debug("login using authCode:"+authHeader);

		tokenParameter.getArgs().set("tenantId", tenantId)
			.set("account", account)
			.set("password", password)
			.set("grantType", grantType)
			.set("refreshToken", refreshToken)
			.set("userType", userType);

		ITokenGranter granter = TokenGranterBuilder.getGranter(grantType);
		logger.debug("get granter:"+ granter);
		AuthUser userInfo = granter.grant(tokenParameter);
		logger.debug("logined user is:"+userInfo);
		if(userInfo!=null){
			logger.debug("logined user is:"+userInfo.getAccount());
			logger.debug("logined user permission size is:"+userInfo.getPermissions().size());
		}
		if (userInfo == null ) {
			return R.fail(TokenUtil.USER_NOT_FOUND);
		}

		return R.data(TokenUtil.createAuthInfo(userInfo));
	}


	/**
	 *
	 * @param authInfo
	 * @return
	 */
	@PostMapping("renewToken")
	public R renewToken(@RequestBody AuthInfo authInfo){

		logger.info("now renew token!");
		String userType = Func.toStr(WebUtil.getRequest().getHeader(TokenUtil.USER_TYPE_HEADER_KEY), TokenUtil.DEFAULT_USER_TYPE);
		String authHeader = WebUtil.getRequest().getHeader("Authorization");
		logger.info("renew using authHeader with header:"+authHeader);
		TokenParameter tokenParameter = new TokenParameter();
		logger.info("renew using account:"+authInfo.getAccount());
		logger.info("renew using usertype:"+userType);
		logger.info("renew using Token:"+authInfo.getAccessToken());
		logger.info("renew using refreshToken:"+authInfo.getRefreshToken());
		logger.info("renew using authCode:"+authHeader);
		logger.info("user expires in:{"+authInfo.getAuthUser().getExpiresIn()+"} now {"+new Date().getTime() +"}");
		ITokenGranter granter = TokenGranterBuilder.getGranter(TokenConstant.REFRESH_TOKEN);
		authInfo.setRefreshToken(authInfo.getAccessToken());
		tokenParameter.getArgs().set("tenantId", authInfo.getTenantId())
			.set("account", authInfo.getAccount())
			.set("grantType", authInfo.getGrantType())
			.set("refreshToken", authInfo.getRefreshToken());
		logger.debug("get granter:"+ granter);

		AuthUser userInfo = granter.grant(tokenParameter);
		userInfo.setClientId(authInfo.getClientId());
		logger.info("renewed user is:"+userInfo);
		if(userInfo!=null){
			logger.info("renewed user is:"+userInfo.getAccount());
			if(userInfo.getPermissions()!=null) logger.info("renewed user permission size is:"+userInfo.getPermissions().size());
		}
		if (userInfo  == null) {
			return R.fail(TokenUtil.USER_NOT_FOUND);
		}
		return R.data(TokenUtil.createAuthInfo(userInfo));
	}


	/**
	 * AuthUser 与 AuthInfo的区别在于，AuthUser只包含用户的基本信息，AuthInfo包含用户的授权信息，
	 * 后期需要梳理，并进行区分，即AuthInfo包括了AuthUser，login与register之后，理论上应返回AuthInfo
	 * @param authUser
	 * @return
	 */
	@PostMapping("/register")
	public R<AuthUser> registerUser(@RequestBody AuthUser authUser){
		AuthUser rUser = userProvider.registerUser(authUser);
		if(Objects.nonNull(rUser)){
			return R.data(authUser);
		}else{
			return R.fail("注册失败！");
		}
	}

	@Operation(
		summary = "获取验证码",
		description = "获取验证码，当以catptcha方式认证时需要"
	)
	@GetMapping("/captcha")
	public R<Kv> captcha() {
		SpecCaptcha specCaptcha = new SpecCaptcha(130, 48, 5);
		String verCode = specCaptcha.text().toLowerCase();
		String key = UUID.randomUUID().toString();
		// 存入redis并设置过期时间为30分钟
		redisUtil.set(CacheNames.CAPTCHA_KEY + key, verCode, 30L, TimeUnit.MINUTES);

		// 将key和base64返回给前端
		logger.debug("生成验证码图片内容："+ specCaptcha);
		logger.debug("生成验证码图片内容："+specCaptcha.toBase64());
		return R.data(Kv.init().set("key", key).set("image", specCaptcha.toBase64()));
	}
	/**
	 *
	 * @param account
	 * @return
	 */
	@PostMapping("logout")
	public R logout(@RequestBody(required = false) String account) {
		return R.data("退出登录成功");
	}

}
